package cn.edu.fudan.data;

import cn.edu.fudan.tools.BinarySearch;
import cn.edu.fudan.type.DataItem;
import cn.edu.fudan.type.XY;

import java.util.ArrayList;
import java.util.List;

public class HandelFeature {

	//number of segments
//	private int N_segment = 100;
	private static BinarySearch bSearch = new BinarySearch();

	private List<XY> mapAbnormal(List<DataItem> data) {   //标准化abnormal

		List<XY> map = new ArrayList<>();

		if (data.size() == 0) {
			return map;
		}

		if (data.size() == 1) {
			XY xy = new XY();
			xy.setX(0);
			xy.setY(1);
			map.add(xy);
			return map;
		}

		long[] interval = new long[data.size() - 1];
		double max = 0;
		double max_index = 0;
		long sum = 0;
		for (int i = 0; i < data.size(); i++) {
			if (i > 0) {
				interval[i - 1] = data.get(i).getTimestamp() - data.get(i - 1).getTimestamp();
				sum += interval[i - 1];
			}
			if (data.get(i).getValue() >= max) {
				max = data.get(i).getValue();
				max_index = data.get(i).getTimestamp();
			}
		}
		double x = 0;
		for (int i = 0; i < data.size(); i++) {
			XY xy = new XY();
			x = (data.get(i).getTimestamp() - max_index) / sum;
			xy.setX(x);
			xy.setY(data.get(i).getValue() / (max > 0 ? max : 1));
			map.add(xy);
		}
		return map;
	}

//	private List<Double> getDataSegments(List<XY> map, int N_segment){
//		
//		List<Double> segment = new ArrayList<>();
//		
//		if(map.size() == 0){
//			for(int i = 0; i < N_segment; i ++){
//				segment.add((double)0);
//			}
//			return segment;
//		}
//		
//		if(map.size() == 1){
//			for(int i = 0; i < N_segment; i ++){
//				if(i == N_segment/2){
//					segment.add(map.get(0).getY());
//					continue;
//				}
//				segment.add((double)0);
//			}
//			return segment;
//		}
//		
//		BigDecimal begin = new BigDecimal(-1);
//		BigDecimal end = begin;
//		BigDecimal interval = new BigDecimal(2).divide(new BigDecimal(N_segment));
//		
//
//		
//		for(int i = 0; i < N_segment; i ++){
//			end = end.add(interval);
//			int count = 0;
//			double value = 0;
//			
//			for(int j = 0; j < map.size(); j ++){
//				BigDecimal x = new BigDecimal(map.get(j).getX());
//				if(begin.compareTo(x) <= 0 && end.compareTo(x) == 1){
//					value += map.get(j).getY();
//					count ++;
//				}
//				if(end.compareTo(new BigDecimal(1)) == 0 && end.compareTo(x) == 0){
//					value += map.get(j).getY();
//					count ++;
//				}
//			}
//			
//			double avg = count>0? value/count:0;
//			segment.add(avg);
//			begin = end;
//		}
//		
//		return segment;
//	}

	private List<Double> getDataSegments(List<XY> map, int N_segment) {   //求论文中的特征F

		List<Double> segment = new ArrayList<>();

		if (map.size() == 0) {
			for (int i = 0; i < N_segment; i++) {
				segment.add((double) 0);
			}
			return segment;
		}

		if (map.size() == 1) {
			for (int i = 0; i < N_segment; i++) {
				if (i == N_segment / 2) {
					segment.add(map.get(0).getY());
					continue;
				}
				segment.add((double) 0);
			}
			return segment;
		}

		double begin = -1;
		double end = begin;
		double interval = 2 / (double) N_segment;   //为什么要用2除段数来当作间隔


		for (int i = 0; i < N_segment; i++) {
			end = end + interval;         //根据每个点一个一个往后推
			int count = 0;
			double value = 0;

			for (int j = 0; j < map.size(); j++) {
				double x = map.get(j).getX();
				if (begin <= x && end > x) {
					value += map.get(j).getY();
					count++;
				}
				if (end == 1 && end == x) {
					value += map.get(j).getY();
					count++;
				}
			}

			double avg = count > 0 ? value / count : 0;
			segment.add(avg);
			begin = end;
		}

		return segment;
	}

	public List<Double> handleFeature(List<DataItem> data, int N_segment) {

		if (data.size() == 0) {
			List<Double> handleData = new ArrayList<>();
			for (int i = 0; i < N_segment; i++) {
				handleData.add((double) 0);
			}
			return handleData;
		}

		List<XY> map = mapAbnormal(data);
		List<Double> handledData = getDataSegments(map, N_segment);    //得到每段当中的F

		return handledData;   //得到每段当中的特征F
	}

	private double getAverage(List<DataItem> data, int begin, int end) { // get average of a window
		double sum = 0;
		for (int i = begin; i < end; i++) {
			sum += data.get(i).getValue();
		}
		double avg = sum / ((double) (end - begin) + 1);
		return avg;
	}

	private double getSquareDevition(List<DataItem> data,int begin,int end){
		double sum = 0;
		double squaresum = 0;
		double average = 0;
		double srd = 0;

		for (int i = 0; i < data.size(); i++) {
			sum += data.get(i).getValue();
		}
		average = sum / (double)data.size();
		for (int i = 0; i < data.size(); i++) {
			squaresum = squaresum+((double)data.get(i).getValue() - average) * (double)(data.get(i).getValue() - (double)average);
		}
		srd = Math.sqrt(squaresum) / (double)(data.size() - 1);

		//return squaresum;
		return srd;
	}

	private List<DataItem> ZNorm(List<DataItem> data){ //Z标准化
		List<DataItem> normdata = new ArrayList<>();
		double sum = 0;
		double squaresum = 0;
		double average = 0;
		double srd = 0;
		if (data.size()==0){
			return data;
		}
		for (int i = 0; i < data.size(); i++) {
			sum += (double)data.get(i).getValue();
		}
		average = sum /(double) data.size();
		for (int i = 0; i < data.size(); i++) {
			squaresum  += ((double)(data.get(i).getValue() - average)) *((double)(data.get(i).getValue() - (double)average));
		}
		srd = Math.sqrt(squaresum) / (double)(data.size() - 1);
		normdata.addAll(data); //初始化
		for (int i = 0; i < data.size(); i++) {
			double t = (data.get(i).getValue()-average)/srd;
			normdata.get(i).setValue((t));
			normdata.get(i).setTimestamp(data.get(i).getTimestamp());

		}
		return normdata;
	}

	public List<Double> handlePaaFeature(List<DataItem> data, int N_segment) {//将找出的观测窗用paa作为特征,
		if (data.size() == 0) {
			List<Double> handleData = new ArrayList<>();
			for (int i = 0; i < N_segment; i++) {
				handleData.add((double) 0);
			}
			return handleData;
		}

		List<Double> paapre = new ArrayList<>();
		//data = ZNorm(data); //将原始序列z标准化
		int threshold_window = data.size() / N_segment;

		long overlap = threshold_window / 2;

		//int index_mid = bSearch.binarySearch(data, data.get(0).getTimestamp() + overlap, 0); //滑动窗中点
		int index_end = bSearch.binarySearch(data, data.get(0).getTimestamp() + threshold_window, 0); //滑动窗末点

		double avg_ini = getAverage(data, 0, index_end);
		DataItem di = new DataItem();
		//di.setTimestamp(index_mid);
		//di.setValue(avg_ini);
		paapre.add(avg_ini);


		int beginIndex = index_end;
		for (int i = 1; i < N_segment-1; i++) {
			//index_mid = bSearch.binarySearch(data, data.get(beginIndex).getTimestamp() + overlap, 0);
			index_end = bSearch.binarySearch(data, data.get(beginIndex).getTimestamp() + threshold_window, 0);
			double avg = getAverage(data, beginIndex, index_end);
			//di.setTimestamp(index_mid);
			//di.setValue(avg);
			paapre.add(avg);
			beginIndex = index_end;
		}
		//index_mid = bSearch.binarySearch(data, data.get((data.size() - 1 + beginIndex) / 2).getTimestamp(), 0);
		index_end = bSearch.binarySearch(data, data.get(data.size() - 1).getTimestamp(), 0);
		double avg = getAverage(data, beginIndex, index_end);
		//di.setTimestamp(index_mid);
		//di.setValue(avg);
		paapre.add(avg);
		return paapre;
	}

	public List<Double> handleSrdFeature(List<DataItem> data, int N_segment) { //分段标准差做特征
		if (data.size() == 0) {
			List<Double> handleData = new ArrayList<>();
			for (int i = 0; i < N_segment; i++) {
				handleData.add((double) 0);
			}
			return handleData;
		}
		List<Double> srdpre = new ArrayList<>();
		//data = ZNorm(data); //将原始序列z标准化
		int threshold_window = data.size() / N_segment;
		int index_end = bSearch.binarySearch(data, data.get(0).getTimestamp() + threshold_window, 0); //滑动窗末点

		double srd_ini= getSquareDevition(data,0,index_end);

		DataItem di = new DataItem();
		//di.setTimestamp(index_mid);
		//di.setValue(avg_ini);
		srdpre.add(srd_ini);;


		int beginIndex = index_end;
		for (int i = 1; i < N_segment-1; i++) {
			//index_mid = bSearch.binarySearch(data, data.get(beginIndex).getTimestamp() + overlap, 0);
			index_end = bSearch.binarySearch(data, data.get(beginIndex).getTimestamp() + threshold_window, 0);
			//index_end = bSearch.binarySearch(data, data.get(beginIndex).getTimestamp() + 6, 0);
			//double avg = getAverage(data, beginIndex, index_end);
			double srd =getSquareDevition(data,beginIndex,index_end);
			//di.setTimestamp(index_mid);
			//di.setValue(avg);
			srdpre.add(srd);
			System.out.println();
			//paapre.add(avg);
			beginIndex = index_end;
		}
		//index_mid = bSearch.binarySearch(data, data.get((data.size() - 1 + beginIndex) / 2).getTimestamp(), 0);
		index_end = bSearch.binarySearch(data, data.get(data.size() - 1).getTimestamp(), 0);
		//double avg = getAverage(data, beginIndex, index_end);
		double srd = getSquareDevition(data,beginIndex,index_end);
		//di.setTimestamp(index_mid);
		//di.setValue(avg);
		srdpre.add(srd);
		return srdpre;
	}

}






